/*
 * Copyright (c) 2007, Nubo Technologies
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 *    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
 *    * Neither the name of the Nubo Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.nubotech.gwt.oss.client.util;

import com.google.gwt.core.client.JavaScriptObject;

/**
 *
 * @author jonnakkerud
 */
public class AesCipher {
    public static int KEY_SIZE = 16;
    public static String PADDING = "0";
    
    public static int SIZE_128 = 16;
    public static int SIZE_192 = 24;
    public static int SIZE_256 = 32;
    
    public enum Mode {OFB, CFB, CBC}
    
    private JavaScriptObject cipherKey;
    private Mode mode;
    private int originalSize;
    private int keySize = SIZE_128;
    private String hexIv = "";
    private String hexKey;
    
    public AesCipher() {
        this.mode = Mode.CBC;
    }
    
    public AesCipher(String key) {
        this(key, Mode.CBC);
    }
    
    public AesCipher(String key, Mode mode) {
        this.mode = mode;
        setCipherKey(key);
    }

    public String encrypt(String clearText) {
        return nativeEncrypt(this.cipherKey, this.mode.ordinal(), this.keySize, nativeToNumber(hexIv), clearText);
    }
    
    public String decrypt(String cipherText) {
        return nativeDecrypt(this.cipherKey, this.mode.ordinal(), this.keySize, nativeToNumber(hexIv), this.originalSize, cipherText);
    }
    
    public void setCipherKey(String key) {
        setCipherKey(key, false);
    }
    
    
    public void setCipherKey(String key, boolean hex) {
        String in = null;
        if (hex == false) {
            // create a hex private key
            this.hexKey = generatePrivateKey(key);
        }
        else {
            this.hexKey = key;
        }
        cipherKey = nativeToNumber(this.hexKey);
    }
    
    public int getOriginalSize() {
        return originalSize;
    }

    public void setOriginalSize(int originalSize) {
        this.originalSize = originalSize;
    }
    
    public String getHexKey() {
        return hexKey;
    }

    public void setHexKey(String hexKey) {
        this.hexKey = hexKey;
    }
    
    public String getHexIv() {
        return hexIv;
    }

    public void setHexIv(String hexIv) {
        this.hexIv = hexIv;
    }
    
    
    public String generatePrivateKey(String in) {
        return nativeGeneratePrivateKey(in, keySize);
    }

    public String generateSharedKey() {
        return nativeGenerateSharedKey(keySize);
    }

    private static native String nativeGenerateSharedKey(int keySize) /*-{
        var key = $wnd.cryptoHelpers.generateSharedKey(keySize);
        return $wnd.cryptoHelpers.toHex(key);                                              
    }-*/;  
    
    private static native String nativeGeneratePrivateKey(String str, int keySize) /*-{
        var key = $wnd.cryptoHelpers.generatePrivateKey(str, keySize);
        return $wnd.cryptoHelpers.toHex(key);                                              
    }-*/;  
    
    // create a number array from the string
    private static native JavaScriptObject nativeToNumber(String str) /*-{
        return $wnd.cryptoHelpers.toNumbers(str);
    }-*/;  
            
    private native String nativeEncrypt(JavaScriptObject key, int mode, int keySize, JavaScriptObject iv, String clearText) /*-{                                                                                                                                                                                                                                                                                  
        var ciph = $wnd.slowAES.encrypt(clearText, mode, key, keySize, iv); 
        if(ciph) {
            var outhex = '';
            for(var i = 0;i < ciph.cipher.length;i++) {
                outhex += $wnd.cryptoHelpers.toHex(ciph.cipher.charCodeAt(i));
            }
            this.@com.nubotech.gwt.oss.client.util.AesCipher::setOriginalSize(I)(ciph.originalsize);                                                                                                                 
	}                                                                                                                                                                                                                                                
        return outhex;        
    }-*/;
    
    private static native String nativeDecrypt(JavaScriptObject key, int mode, int keySize, JavaScriptObject iv, int origLen, String encryptedText) /*-{                                                                                                                                       
        var innumbers = $wnd.cryptoHelpers.toNumbers(encryptedText);
	var instring = '';
	for(var i = 0;i < innumbers.length; i++)
	{
		instring += String.fromCharCode(innumbers[i])
	}                                                                            
        return $wnd.slowAES.decrypt(instring, origLen, mode, key, keySize, iv); 
    }-*/;  
    
    
}
